package mail

import (
	"encoding/base64"
	"errors"
	"fmt"
	"gopkg.in/gomail.v2"
	"log"
	"net/smtp"
	"strconv"
	"strings"
	"sync"
)

type Services struct {
	Id       string
	Host     string
	Port     int
	Username string
	Password string
}

// New 实例邮件发送
func New(host, username, password string, port int) *Services {
	return &Services{
		Host:     host,
		Port:     port,
		Username: username,
		Password: password,
	}
}

// SendMail 发送邮件
func (s *Services) SendMail(subject, body string, emails ...string) error {
	if len(emails) == 0 {
		return errors.New(`email addresses are empty`)
	}

	var (
		wg   sync.WaitGroup
		errs []error
		mu   sync.Mutex
	)

	// 迭代发送邮件
	for _, email := range emails {
		wg.Add(1)
		go s.sendEmailHandler(email, subject, body, &wg, &mu, &errs)
	}

	// 等待执行完成
	wg.Wait()
	// 处理错误信息
	if len(errs) > 0 {
		return fmt.Errorf("failed to send some emails: %v", errs)
	}
	return nil
}

// RawSendMail 使用原生库发送邮件
func (s *Services) RawSendMail(subject, message string, emails ...string) error {
	if len(emails) == 0 {
		return errors.New(`email addresses are empty`)
	}

	// 拼接发送地址
	addr := s.Host
	if s.Port > 0 {
		addr += ":" + strconv.Itoa(s.Port)
	}

	// 设置授权
	auth := smtp.PlainAuth(s.Id, s.Username, s.Password, s.Host)

	// 发送消息
	if err := smtp.SendMail(addr, auth, s.Username, emails, s.processRawMessage(emails, subject, message)); err != nil {
		return err
	}
	return nil
}

// sendEmailHandler 封装了邮件发送的逻辑
func (s *Services) sendEmailHandler(email string, subject, body string, wg *sync.WaitGroup, mu *sync.Mutex, errs *[]error) {
	defer wg.Done()
	message := gomail.NewMessage()
	message.SetHeader("From", s.Username)
	message.SetHeader("To", email)
	message.SetHeader("Subject", subject)
	message.SetBody("text/plain", body)

	service := gomail.NewDialer(s.Host, s.Port, s.Username, s.Password)
	if err := service.DialAndSend(message); err != nil {
		mu.Lock()
		*errs = append(*errs, fmt.Errorf("failed to send email to %s: %v", email, err))
		mu.Unlock()
		log.Fatalf(`failed to send email to %s: %v`, email, err)
	}
}

// processRawMessage 处理原始消息
func (s *Services) processRawMessage(emails []string, subject, message string) []byte {
	return []byte(fmt.Sprintf(
		"To: %s \r\nFrom: %s \r\n"+
			"Subject: =?UTF-8?B? %s ?=\r\n"+
			"MIME-Version: 1.0\r\n"+
			"Content-Type: text/plain; charset=\"UTF-8\"\r\n"+
			"Content-Transfer-Encoding: base64\r\n\r\n %s\r\n",
		strings.Join(emails, ","),
		s.Username,
		base64.StdEncoding.EncodeToString([]byte(subject)),
		base64.StdEncoding.EncodeToString([]byte(message)),
	))
}
